package com.example.run_game;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;

import org.xml.sax.SAXException;

import android.content.Context;
import android.location.Location;
import android.util.Log;
 
public class Utils {
	
	private static final Map<String, String> audioMap = new HashMap<String, String>();
    static {
        audioMap.put("Archeology Department", "archaeology_department.mp3");
        audioMap.put("runFast1000", "big_speed_bonus.mp3");
        audioMap.put("extra800", "big_xp_bonus.mp3");
        audioMap.put("Biology Building", "biology_building.mp3");
        audioMap.put("Bottom of Woodland Road", "bottom_woodland.mp3");
        audioMap.put("Circular Road overlook", "circular_road.mp3");
        audioMap.put("Clifton College Gates", "clifton_college.mp3");
        audioMap.put("Chemistry Department", "cloister.mp3");
        audioMap.put("1.5xpoints", "double_xp.mp3");
        audioMap.put("Ladies Mile Middle", "ladies_mile.mp3");
        audioMap.put("MVB Carpark Entrance", "mvb_carpark.mp3");
        audioMap.put("or", "or.mp3");
        audioMap.put("The Promenade", "promenade.mp3");
        audioMap.put("Queens Building Back Door", "queens_building.mp3");
        audioMap.put("Redgrave Theatre", "redgrave_theatre.mp3");
        audioMap.put("Sea Walls", "sea_walls.mp3");
        audioMap.put("runFast800", "speed_bonus.mp3");
        audioMap.put("Clifton Suspension Bridge", "suspension_bridge.mp3");
        audioMap.put("The Woods", "the_woods.mp3");
        audioMap.put("2xPoints", "triple_xp.mp3");
        audioMap.put("Watertower", "water_tower.mp3");
        audioMap.put("Westbury Park Roundabout", "westbury_park.mp3");
        audioMap.put("Wills Memorial Tower", "wills_memorial.mp3");
        audioMap.put("Wills Memorial Steps", "wills_steps.mp3");
        audioMap.put("extra500", "xp_bonus.mp3");
        audioMap.put("Stoke Road Crossroads", "stoke_road.mp3");
        audioMap.put("Side Steps to Queens", "queens_side_steps.mp3");
        audioMap.put("MVB Bike Rack", "bike_rack.mp3");
        audioMap.put("Starting Point", "almost_done.mp3");
    }
    
    public static String getAudio(String key)
    {
    	return audioMap.get(key);
    }
	
    public static void CopyStream(InputStream is, OutputStream os)
    {
        final int buffer_size=1024;
        try
        {
            byte[] bytes=new byte[buffer_size];
            for(;;)
            {
              int count=is.read(bytes, 0, buffer_size);
              if(count==-1)
                  break;
              os.write(bytes, 0, count);
            }
        }
        catch(Exception ex){}
    }
    
    public static double RouteDistance(ArrayList<Location>route)
    {
    	double length = 0.0;
    	for (int i=0; i < route.size()-1; i++)
    	{
    		Location loc1 = route.get(i);
    		Location loc2 = route.get(i+1);
    		double dist = distBetweenPoints(loc1,loc2);
    		length+=dist;
    	}
		return length;
    }

	private static double distBetweenPoints(Location loc1, Location loc2)
	{
		double R = 6378100.0;
		double dLat = d2r(loc2.getLatitude() - loc1.getLatitude());
		double dLon = d2r(loc2.getLongitude() - loc1.getLongitude());
		
		double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
				Math.cos(d2r(loc1.getLatitude())) * Math.cos(d2r(loc2.getLatitude())) *
				Math.sin(dLon/2) * Math.sin(dLon/2);
		double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
		double d = R * c;
		return d;
	}

	private static double d2r(double d)
	{
		return d *(Math.PI/180);
	}

	public static ArrayList<Location> addSpeedsToRoute(
			ArrayList<Location> mRoute)
	{
		ArrayList<Location> result = new ArrayList<Location>();
		if (mRoute.size() < 2)
			return result;
		result.add(mRoute.get(0));
		for (int i = 1; i < mRoute.size(); i++)
		{
			Location prev = mRoute.get(i-1);
			Location loc = mRoute.get(i);
			double kmdistance = Utils.distBetweenPoints(prev,loc)/1000;
			long timediff = loc.getTime() - prev.getTime();
			double htimediff = (1.0*timediff / (1000*60*60));
			double kphSpeed = kmdistance / htimediff;
			Log.d("speed",Double.toString(kphSpeed));
			loc.setSpeed((float) kphSpeed);
			result.add(loc);
		}
		return result;
	}

	public static void addNewRouteToIndex(ArrayList<Location> route, String filename, Context context)
	{
		FileInputStream fis;
		try
		{
			fis = context.openFileInput("RunHistory");
		} catch (FileNotFoundException e1)
		{
			createIndexXML(context);
			return;
		}
		StringBuffer fileContent = new StringBuffer("");	       
        try
		{
			int ch;
			while ((ch = fis.read()) != -1)
			{
				fileContent.append((char)ch);
			}
		} catch (IOException e)
		{
			
			e.printStackTrace();
		}
		
		// add new route
		String contents = fileContent.toString();
		Log.d("history","before: " + contents);
		contents = contents.replaceAll("</history>", "");
		System.out.println("route:"+route);
		System.out.println("filename:"+filename);
		contents += routeXML(route, filename);
		contents += "</history>\n";	
		Log.d("history","after: " + contents);	
		writeToXML(contents, context);
	}
	
	public static String routeXML(ArrayList<Location> route, String filename)
	{
		String finalString = "";
		if (route.size() > 0)
		{
			DecimalFormat df = new DecimalFormat("#.##");
			finalString = "";
			finalString += "<run>\n";
			long startTime = route.get(0).getTime();
			long duration = route.get(route.size()-1).getTime() - route.get(0).getTime();
			finalString += "<id>"+filename+"</id>\n";
			finalString += "<start>"+startTime+"</start>\n";
			finalString += "<distance>"+df.format(Utils.RouteDistance(route)/1000)+"km</distance>\n";
			finalString += "<time>"+Utils.formatMilliTime(duration, false)+"</time>\n";
			finalString += "</run>\n\n";
		}
		return finalString;
	}
	
	public static void createIndexXML(Context context)
	{
		File f = context.getFilesDir();
		ArrayList<String> tobedeleted = new ArrayList<String>();		
		String finalString = "";
		ArrayList<String> filenames = new ArrayList<String>();
		for (File fi : f.listFiles())
		{
			if (fi.getName().startsWith("route"))
				filenames.add(fi.getName());
		}
		TreeMap<Long,String> timeSortedXML = new TreeMap<Long,String>();
		for (String filename : filenames)
		{
			FileInputStream fis;
			if (!filename.startsWith("route"))
				continue;
			try
			{
				fis = context.openFileInput(filename);
				ArrayList<Location> route = XMLParser.parseXMLRoute(fis);
				if (route.size() > 0)
				{
					timeSortedXML.put(route.get(0).getTime(), routeXML(route, filename));
				}
				else
					tobedeleted.add(filename);
				
					
			} catch (FileNotFoundException e)
			{
			
				e.printStackTrace();
			} catch (IOException e)
			{
				
				e.printStackTrace();
			} catch (SAXException e)
			{
				
				tobedeleted.add(filename);
				e.printStackTrace();
			}
		}
		String result = "<history>\n";
		for (Entry<Long,String> e : timeSortedXML.entrySet())
		{
			System.out.println("writing "+e.getKey());
			result += e.getValue();
		}
		result += "</history>\n";
		writeToXML(result, context);
	}
	
	private static void writeToXML(String xmlString, Context context)
    {
    	try {
    		context.deleteFile("RunHistory");
			FileOutputStream fos = context.openFileOutput("RunHistory",Context.MODE_APPEND);
			System.out.println(xmlString);
			fos.write(xmlString.getBytes());
			fos.close();
		} catch (IOException e) {
			Log.e("RunHistory.java", "ROUTE FILE COULD NOT BE ACCESSED\n");
			e.printStackTrace();
		}
    }

	public static String formatMilliTime(long millis, boolean showMillis)
	{
		int seconds = (int) (millis / 1000);
		int minutes = seconds / 60;
		int hours 	= minutes / 60;
		minutes 	= minutes % 60;
		seconds     = seconds % 60;
		int mil 	= (int) (millis - hours*60*60*1000 - minutes*60000 - seconds*1000);

		String result;
		
		if (showMillis)
			result = hours +
				(minutes<10?":0":":") + minutes + 
				(seconds<10?":0":":") + seconds +
				(mil<10?".00":mil<100?".0":".") + mil;
		else
			result = hours +
			(minutes<10?":0":":") + minutes + 
			(seconds<10?":0":":") + seconds;		
		
		return result;
	}
	
	public static String readXML(String filename, Context context)
	{
		FileInputStream f = null;
		try
		{
			f = context.openFileInput(filename);
		} catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}
		int ch;
		StringBuffer result = new StringBuffer("");
		try
		{
			while ((ch = f.read()) != -1)
				result.append((char)ch);
		} catch (IOException e)
		{
			
			e.printStackTrace();
		}
		return result.toString();
	}
	
	public static double totalDistanceEver(Context context)
	{
		double distance = 0;
		for (File f : context.getFilesDir().listFiles())
		{
			if (f.getName().startsWith("route"))
			{
				try
				{
					distance += Utils.RouteDistance(XMLParser.parseXMLRoute(context.openFileInput(f.getName())));
				} catch (FileNotFoundException e)
				{
				
					e.printStackTrace();
				} catch (SAXException e)
				{
				
					e.printStackTrace();
				}
			}
		}
		return distance;
	}
	
	public static int totalNumberRuns(Context context)
	{		
		int count = 0;
		for (File f : context.getFilesDir().listFiles())
		{
			if (f.getName().startsWith("route"))
			{
				count++;
			}
		}
		return count;
	}
	
	public static long totalTimeEver(Context context)
	{
		long time = 0;
		for (File f : context.getFilesDir().listFiles())
		{
			if (f.getName().startsWith("route"))
			{
				try
				{
					ArrayList<Location> r = XMLParser.parseXMLRoute(context.openFileInput(f.getName()));
					long diff = r.get(r.size()-1).getTime() - r.get(0).getTime();
					time += diff;
				} catch (FileNotFoundException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (SAXException e)
				{
				
					e.printStackTrace();
				}
			}
		}
		return time;
	}

	public static int calcCalories(double distanceMoved, int weight)
	{
       	double cal = (0.95*weight + 0.84)*distanceMoved/1000*1.05;
		return (int)cal;
	}
}